package com.feishi.core.util.common;


import com.feishi.core.util.common.bean.CallFunction;

import java.io.*;
import java.nio.charset.Charset;
import java.util.*;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: caixq
 * Date: 2019-03-28
 * Time: 上午10:26
 */
@SuppressWarnings("unchecked")
public class BitReaderWithCsv {
    private static String MSG_Name="msgname";
    private static String Signal_Name="itemname";
    private static String CAN_ID="msgid";
    private static String Start_Bit="startbit";
    private static String Size="size";
    private static String Data_Type="datatype";
    private static String Factor="factor";
    private static String Offset="offset";
    private static String Initial_Value="initialvalue";
    public static Charset charset=Charset.defaultCharset();


    private static int type=1;//intel包结构

    public static CsvConfig loadCsvConfig(String s) throws IOException {
        CsvConfig csvConfig=new CsvConfig();
        csvConfig.load(s);
        return csvConfig;
    }

    public static CsvConfig loadCsvConfig(InputStream inputStream) throws IOException {
        CsvConfig csvConfig=new CsvConfig();
        csvConfig.load(inputStream);
        return csvConfig;
    }


    public static class CsvConfig{
        Map<String,MsgConfig> msgConfigMap=new HashMap<>();
        Map<String,MsgConfig> canIdConfigMap=new HashMap<>();
        Map<String,List<CallFunction.voidFunction1<Msg>>> handlers=new HashMap<>();
        public void addHandler(String msgName, CallFunction.voidFunction1<Msg> handler){
            List<CallFunction.voidFunction1<Msg>> list = handlers.get(msgName);
            if(list==null){
                list=new ArrayList<>();
                handlers.put(msgName,list);
            }
            list.add(handler);
        }


        public void load(String file) throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(Thread.currentThread().getContextClassLoader().getResourceAsStream(file)));//换成你的文件名
            load(Thread.currentThread().getContextClassLoader().getResourceAsStream(file));
        }

        public void load(InputStream inputStream) throws IOException {
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));//换成你的文件名

            String titleStr = reader.readLine();
            Map<String,Integer> titleIndex=new HashMap<>();
            String[] titles = titleStr.split(",");
            for (int i = 0; i < titles.length; i++) {
                titleIndex.put(titles[i].toLowerCase().replaceAll("\\s+",""),i);
            }
            String line = null;
            while((line=reader.readLine())!=null){
                String item[] = line.split(",");//CSV格式文件为逗号分隔符文件，这里根据逗号切分
                ItemConfig itemConfig=new ItemConfig();

                itemConfig.startBit=Integer.parseInt(item[titleIndex.get(Start_Bit)]);
                itemConfig.size=Integer.parseInt(item[titleIndex.get(Size)]);
                itemConfig.factor=Double.parseDouble(item[titleIndex.get(Factor)]);
                itemConfig.offset=Double.parseDouble(item[titleIndex.get(Offset)]);
                String initValue = item[titleIndex.get(Initial_Value)];
                itemConfig.initialValue=initValue.indexOf("0x")!=-1?(Integer.parseInt(initValue.replace("0x",""))):Integer.parseInt(initValue);
                itemConfig.type=item[titleIndex.get(Data_Type)];

                MsgConfig msgConfig=msgConfigMap.get(item[titleIndex.get(MSG_Name)]);
                if(msgConfig==null){
                    msgConfig=new MsgConfig(item[titleIndex.get(MSG_Name)],item[titleIndex.get(CAN_ID)]);
                    msgConfigMap.put(item[titleIndex.get(MSG_Name)],msgConfig);
                    canIdConfigMap.put(item[titleIndex.get(CAN_ID)].toLowerCase(),msgConfig);
                }
                msgConfig.detail.put(item[titleIndex.get(Signal_Name)],itemConfig);
            }
        }

        public void execHandler(String canId, String content) {
            String msgName=canIdConfigMap.get(canId).canId;
            List<CallFunction.voidFunction1<Msg>> list = handlers.get(msgName);
            if(list!=null){
                byte[] bs = ByteUtil.getBytesFromHex(content);

                ByteUtil.BitByte bitByte=new ByteUtil.BitByte(bs);
                for (CallFunction.voidFunction1<Msg> callBack : list) {
                    callBack.execute(new Msg(this.msgConfigMap.get(msgName).detail,bitByte));
                }
            }
        }
        public void execHandler(String canId, byte[] bs) {
            String msgName=canIdConfigMap.get(canId).msgName;
            List<CallFunction.voidFunction1<Msg>> list = handlers.get(msgName);
            if(list!=null){
                ByteUtil.BitByte bitByte=new ByteUtil.BitByte(bs);
                for (CallFunction.voidFunction1<Msg> callBack : list) {
                    callBack.execute(new Msg(this.msgConfigMap.get(msgName).detail,bitByte));
                }
            }
        }
        public Msg getMsg(String msgName, byte[] bs) {
            if (this.msgConfigMap.get(msgName)==null){
                return null;
            }
            ByteUtil.BitByte bitByte=new ByteUtil.BitByte(bs);
            Map<String, ItemConfig> config = this.msgConfigMap.get(msgName).detail;
            return new Msg(config,bitByte,0);
        }

        int type=0;
        public   MsgBuild msgBuild(String msgName){
            MsgConfig config=msgConfigMap.get(msgName);


            MsgBuild msgBuild=new MsgBuild(config.detail,config.canId,type);
            return msgBuild;
        }
        public void printInterface(){
            System.out.println("");
            for (Map.Entry<String, MsgConfig> stringMsgConfigEntry : msgConfigMap.entrySet()) {
                System.out.println("\tinterface "+stringMsgConfigEntry.getKey()+"{\n");
                System.out.println("\t\tString name=\""+stringMsgConfigEntry.getKey()+"\";\n");
                for (Map.Entry<String, ItemConfig> stringItemConfigEntry : stringMsgConfigEntry.getValue().detail.entrySet()) {
                    System.out.println("\t\tString "+stringItemConfigEntry.getKey().replace(" ","_")+"=\""+stringItemConfigEntry.getKey()+"\";\n");
                }
                System.out.println("}\n");

            }
        }

    }
    public static class MsgConfig{
        public Map<String,ItemConfig> detail=new HashMap<>();
        public String msgName;
        public String canId;

        public MsgConfig(String msgName, String canId) {
            this.msgName = msgName;
            this.canId = canId;
        }
    }
    public static class Msg{
        public Map<String,ItemConfig> detail;
        public ByteUtil.BitByte bitByte;
        public int type=1;

        public Msg(Map<String, ItemConfig> detail, ByteUtil.BitByte bitByte) {
            this.detail = detail;
            this.bitByte = bitByte;
        }

        public Msg(Map<String, ItemConfig> detail, ByteUtil.BitByte bitByte, int type) {
            this.detail = detail;
            this.bitByte = bitByte;
            this.type = type;
        }

        public Number getValue(String signalName) {
            ItemConfig config=detail.get(signalName);
            if(config==null){
                System.out.println("非法信号名称");
                return null;
            }
            byte[] bs = getBytes(signalName);
            switch (config.type){
                case "ENUM":
                    return ByteUtil.toInt(bs);
                case "UNS":
                    return ByteUtil.toInt(bs)*config.factor+config.offset;
            }
            return null;
        }

        public  byte[] getBytes( String signalName) {
            ItemConfig config=detail.get(signalName);
            if(config==null){
                System.out.println("非法信号名称");
                return null;
            }
            return getBytes(config);
        }
        public  byte[] getBytes(ItemConfig config) {
            int start=config.startBit;
            if(type==1){
                return bitByte.readRightIntel(start, config.size);
            }
            return bitByte.readRight(start, config.size);
        }
        public  int getInt( String signalName) {
            return ByteUtil.toInt(getBytes(signalName));
        }

        public  long getLong( String signalName) {
            return ByteUtil.toLong(getBytes(signalName));
        }
        public  double getDouble( String signalName) {
            ItemConfig config=detail.get(signalName);
            if(config==null){
                System.out.println("非法信号名称");
                return 0;
            }
            return ByteUtil.toLong(getBytes(config))*config.factor+config.offset;
        }
        public String getString(String signalName) {
            return new String(getBytes(signalName),charset);
        }
        public String getString(String signalName, Charset charset) {
            return new String(getBytes(signalName),charset);
        }
        public String getBCDString(String signalName) {
            return ByteUtil.bcdToString(getBytes(signalName));
        }

    }
    public static class ItemConfig{
        public int startBit;
        public int size;
        public double factor;
        public double offset;
        public String type;

        public int initialValue;

        public ItemConfig() {
        }

        public ItemConfig(int startBit, int size, double factor, double offset) {
            this.startBit = startBit;
            this.size = size;
            this.factor = factor;
            this.offset = offset;
        }
    }




    public static class MsgBuild{
        public Map<String,ItemConfig> detail;
        public String canId;
        public int type;
        public Map<String,Object> values=new HashMap<>();

        public MsgBuild(Map<String, ItemConfig> detail, String canId, int type) {
            this.detail = detail;
            this.canId = canId;
            this.type = type;
        }

        public void setValue(String signName, Object value){
            values.put(signName,value);
        }
        public byte[] buildContent(){
            int max=0;
            for (ItemConfig itemConfig : detail.values()) {
                if(type==1)
                    max=max>(itemConfig.startBit)?max:(itemConfig.startBit);
                else
                    max=max>(itemConfig.startBit+itemConfig.size)?max:(itemConfig.startBit+itemConfig.size);
            }
            byte[] bs=new byte[max/8+1];
            ByteUtil.BitByte bitByte=new ByteUtil.BitByte(bs);
            for (Map.Entry<String, ItemConfig> stringItemConfigEntry : detail.entrySet()) {
                Object value=values.get(stringItemConfigEntry.getKey());
                ItemConfig config=stringItemConfigEntry.getValue();
                if(value==null){
                    value=ByteUtil.intToByte4(config.initialValue);
                }
                if (type==1){
                    bitByte.writeRightIntel(config.startBit,config.size,getValue(config,value));
                }else {
                    bitByte.writeRight(config.startBit,config.size,getValue(config,value));
                }

            }
            return bitByte.get();
        }
        public byte[] buildSocketPacket(){
            byte[] content=buildContent();
            byte[] header=new byte[]{(byte) content.length};
            /**
             * 左补0至8位
             * */
            String hex=this.canId.replaceAll("^0x([\\da-fA-F]+)$","00000000$1").replaceAll("^.*([\\da-fA-F]{8})$","$1");
            byte[] canId=ByteUtil.getBytesFromHexWithoutSpace(hex,2);

//            System.out.println("hex:::"+hex+"::"+this.canId);
            return ByteUtil.concat(header,canId,content);


        }
        public String buildCmd(){
            StringBuffer sb=new StringBuffer();
            sb.append(canId).append(" ").append(ByteUtil.getHexString(buildContent()));
            return sb.toString();
        }
        byte[] bs=new byte[0];
        private byte[] getValue(ItemConfig config, Object v) {
            switch (config.type){
                case "ENUM":
                    return (byte[]) v;
                case "UNS":

                    double value=0;
                    if (bs.getClass().isInstance(v)){
                        return (byte[]) v;
                    }else  if (v instanceof String){
                        return ((String)v).getBytes(charset);
                    }else  if (v instanceof Integer){
                        value= (int) v;
                    }else if (v instanceof Double){
                        value= (double) v;
                    }else if (v instanceof Float){
                        value= (float) v;
                    }else if(v instanceof Long){
                        value= (float) v;
                    }
                    double val=Math.round(((value-config.offset)/config.factor));

//                    System.out.println("out values::"+value+":"+config.offset+":"+config.factor+"::"+val+":"+String.format("%X",(long)val)+":"+ByteUtil.getHexString(ByteUtil.longToByte8((long)val)));
                    return  ByteUtil.longToByte8((long)val);
            }
            return null;
        }

    }


    public static void handler(String msg, CsvConfig config) {
        /**
         * 非CAN消息体内容，不作解析
         * */
        if(!msg.matches("^<0x[\\da-f]+>\\s+\\[\\d\\](\\s+[\\da-fA-F]+)+$")){
//            System.out.println(false);
            return;
        }
        String canId=msg.substring(1,msg.indexOf(">"));
        String content=msg.substring(msg.indexOf("] ")+2);
        config.execHandler(canId,content);
    }

    public static void handler(byte[] msg, CsvConfig config) {

        ByteUtil.getHexString(msg);
        String canId=("0x"+ByteUtil.getHexString(msg,1,5).replaceAll("^0*","")).toLowerCase();
        byte[] content=ByteUtil.get(msg,5,msg.length-5);
//        System.out.println("canId：：："+canId);
//        System.out.println("content：：：："+ByteUtil.getHexString(content));
//        System.out.println(canId);
        config.execHandler(canId,content);
    }

    private static byte[] bf;
    public static void tryRead(byte[] bs, CallFunction.voidFunction1 callback) {
        int length=(bs[0]&0b1111)+5;
//        System.out.println(length);

        if(length==bs.length){
            callback.execute(bs);
            bf=null;
            return ;
        }
        if(bf==null){
            if(length<bs.length){
                byte[] b = new byte[ bs.length-length];
                byte[] bytes=new byte[length];
                System.arraycopy(bs,length,b,0,b.length);
                System.arraycopy(bs,0,bytes,0,bytes.length);
                callback.execute(bytes);
                tryRead(b,callback);
                return ;
            }else{
                bf=bs;
            }
        }else {
            byte[] out = ByteUtil.concat(bf, bs);
            tryRead(out,callback);
        }
    }


    public static void main(String[] args){

        try {
            CsvConfig config=new CsvConfig();
            config.type=0;
            config.load(new FileInputStream(new File("/home/caixq/Desktop/config.csv")));
            MsgBuild build = config.msgBuild("baseUpload");
            build.setValue("sendTime",((((19*12+6)*31+14)*24+17)*60+50)*60+31);
            ;
            build.setValue("vol",22);
            System.out.println(ByteUtil.getHexString(build.buildContent()));
            Msg msg = config.getMsg("baseUpload", build.buildContent());
            int sendTime=msg.getInt("sendTime");
            System.out.println(sendTime);

            System.out.println(sendTime/60/60/24/31/12+":"+sendTime/60/60/24/31%12+":"+sendTime/60/60/24%31+":"+sendTime/60/60%24+":"+sendTime/60%60+":"+sendTime%60);
            System.out.println(msg.getInt("vol"));
            System.out.println(msg.getDouble("vol"));
//            config.printInterface();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
